/*
    Load Store Unit
*/
module lsu(
	input 	wire 	[31:0]	addr,
	input 	wire 	[31:0]  lsuDataIn,              // Data to mem
	input 	wire          	loadOp,
	input 	wire          	storeOp,
	output 	wire         	lsuDone,
	output 	wire 	[31:0]  lsuDataOut,		        // Data from mem
		
	input   wire    [02:0]  func,

	output 	wire 	[31:0] 	WB_ADRo,			
	output 	wire 	[31:0]  WB_DATo,
	input 	wire 	[31:0]  WB_DATi,
	output 	wire         	WB_WEo,				
	output 	wire         	WB_CYCo,											
	input 	wire          	WB_ACKi

);
/*
    Because the width of the data bus is 32, the address is divided into the following two parts.
    |0000|0000|0000|0000|0000|0000|0000|  0 0 0 0  |
    |--------------wordAddr------------|--byteAddr-|
*/
    wire            [29:0]  wordAddr;   // word address in perips
    wire            [01:0]  byteAddr;   // byte address in word
    




	
	
    
    // WB_DATo is store data.
    // This name is because WishBone bus is used. You can also modify it to any bus you want.
    // The 'WB' is short for WishBone
	assign WB_DATo  		=	( storeOp && (func[1] == func[0] && byteAddr == 2'b00 )  ? {24'b0,lsuDataIn[07:0]}        : 32'b0) |
                                ( storeOp && (func[1] == func[0] && byteAddr == 2'b01 )  ? {16'b0,lsuDataIn[07:0],08'b0}  : 32'b0) |
                                ( storeOp && (func[1] == func[0] && byteAddr == 2'b10 )  ? {08'b0,lsuDataIn[07:0],16'b0}  : 32'b0) |
                                ( storeOp && (func[1] == func[0] && byteAddr == 2'b11 )  ? {lsuDataIn[07:0],24'b0}        : 32'b0) |
                                ( storeOp &&  func[0] && byteAddr == 2'b00               ? {16'b0,lsuDataIn[15:0]}        : 32'b0) |
                                ( storeOp &&  func[0] && byteAddr != 2'b00               ? {lsuDataIn[15:0],16'b0}        : 32'b0) |
                                ( storeOp &&  func[1]                                    ? lsuDataIn                      : 32'b0) ;

    // Wishbone control signal,you can also modify it to any bus you want.
    assign WB_ADRo			=	(loadOp | storeOp) ? {2'b00,wordAddr} 	: 32'b0;
	assign WB_WEo   		=	storeOp;
	assign WB_CYCo			=	loadOp | storeOp;
	assign lsuDone			=	WB_ACKi;
	
    // lsuDataOut is load data
	assign lsuDataOut	=	(func == 3'b000 && byteAddr == 2'b00 ? {{24{WB_DATi[07]}}, WB_DATi[07:00]} : WB_DATi) |   // LB
                            (func == 3'b000 && byteAddr == 2'b01 ? {{24{WB_DATi[15]}}, WB_DATi[15:08]} : WB_DATi) |
                            (func == 3'b000 && byteAddr == 2'b10 ? {{24{WB_DATi[23]}}, WB_DATi[23:16]} : WB_DATi) |
                            (func == 3'b000 && byteAddr == 2'b11 ? {{24{WB_DATi[31]}}, WB_DATi[31:24]} : WB_DATi) |

							(func == 3'b001 && byteAddr == 2'b00 ? {{16{WB_DATi[15]}}, WB_DATi[15:00]} : WB_DATi) |   // LH
							(func == 3'b001 && byteAddr != 2'b00 ? {{16{WB_DATi[31]}}, WB_DATi[31:16]} : WB_DATi) |

                            (func == 3'b000 && byteAddr == 2'b00 ? {24'b0            , WB_DATi[07:00]} : WB_DATi) |   // LBU
                            (func == 3'b000 && byteAddr == 2'b01 ? {24'b0            , WB_DATi[15:08]} : WB_DATi) |
                            (func == 3'b000 && byteAddr == 2'b10 ? {24'b0            , WB_DATi[23:16]} : WB_DATi) |
                            (func == 3'b000 && byteAddr == 2'b11 ? {24'b0            , WB_DATi[31:24]} : WB_DATi) |

							(func == 3'b001 && byteAddr == 2'b00 ? {16'b0            , WB_DATi[15:00]} : WB_DATi) |   // LHU
							(func == 3'b001 && byteAddr != 2'b00 ? {16'b0            , WB_DATi[31:16]} : WB_DATi) ;
                                                                                                                      // LW


endmodule